package global;

import heap.Heapfile;
import heap.InvalidSlotNumberException;
import heap.Tuple;

import java.io.*;

import va.VAIndexAdapter;
import va_file.*;

/*
 * @author Mark Yates
 * This class implements the batchinsert command
 */
public class Batch implements GlobalConst
{
	// new batchcreate command for project 3, will create a new DB and new relation
	public static boolean Create(String dataFileName, String relName) 
			throws InvalidSlotNumberException, Exception
	{
		Schema ts = insertData(dataFileName, relName, false);
	    return (ts != null);
	}

	/*
	 * new batchinsert command for project 3, will use an existing DB. if relation already exists,
	 * will insert tuples into existing relation. will create relation if doesn't exists. will
	 * update existing indexes on relation as well. indexes can be recreated rather than updated
	 * by passing recreateIndexes = true.
	 */
	public static boolean Insert(String dataFileName, String relName, boolean recreateIndexes) 
			throws InvalidSlotNumberException, Exception
	{
		Schema ts = insertData(dataFileName, relName, !recreateIndexes);
		boolean successful = (ts != null);
	    
	    if (successful && recreateIndexes)
	    {
	    	// re-create indexes
	    	BatchIndex indexer = new BatchIndex(relName, true);
	    	successful = indexer.CreateIndexes(ts);
	    	if (successful)
	    	{
		    	System.out.format("%d indexes re-created.\n", indexer.IndexCount());
	    	}
            indexer.close();
	    }
	    return ts!=null;		
	}

	// old batchinsert command from project 2, will create a new DB, new relation and VAFile indexes
	public static Schema OldInsert(String dataFileName, String relName) 
			throws InvalidSlotNumberException, Exception
	{
		Schema ts = insertData(dataFileName, relName, false);
	    
		// create default indexes
		int[] vflds = getVectorFlds(ts);
		for (int i=0; i<vflds.length; i++)
		{
			IDXSchema index = new IDXSchema(relName, vflds[i], "H", VAFile.BITS_PER_DIMENSTION);
            VAIndexAdapter adapter = new VAIndexAdapter(index, true);
			BatchIndex.createVAIndex(ts, adapter);
		}
	    return ts;
	}
	// does all the work of inserting a tuple into a relation.
	private static Schema insertData(String dataFileName, String relName, boolean updateIndexes) throws InvalidSlotNumberException, Exception
	{
		boolean successful = true;

		BatchIndex indexer = updateIndexes ? new BatchIndex(relName, false) : null;
		
		// read the file with the data to insert
		BufferedReader br = new BufferedReader(new FileReader(dataFileName));
	    int attrNum = 0;
	    attrNum = Integer.parseInt(br.readLine().trim());	// number of attributes
	    String line = br.readLine().trim();		// attribute types
	    Schema ts;
	    if ((ts = getAttribTypes(line, attrNum)) == null)
	    {
	    	successful = false;
	    }
	    ts.dbName = relName;
	    // Create unsorted data file
	    RID             rid;
	    Heapfile        f = null;
	    try 
	    {
	      f = new Heapfile(relName);
	    }
	    catch (Exception e) {
	      System.err.println("Could not open/create database file.");
	      successful = false;
	    }
	    
	    Tuple t=null;
	    if ( !successful || (t=prepTuple(attrNum, ts.attrType, ts.attrSize)) == null )
	    {
	    	successful = false;
	    }
	    // read values from file and insert into DB
	    int fldCnt = 0, tupleCnt=0;
	    String[] values = new String[attrNum];
	    while (successful)
	    {
	    	if ( (line = br.readLine()) == null )
	    	{
	    		if (fldCnt != 0)
	    		{
	    			System.err.println("Invalid data file. There were not enough line");
	    		}
	    		break;
	    	}
	    	values[fldCnt] = line;
	    	fldCnt++;
	    	if ( fldCnt == attrNum )
	    	{
	    		fldCnt = 0;
			    if ( (t=getTuple(t, ts, values, attrNum)) == null)
			    {
			    	successful = false;
			    }
			    else
			    {
			    	// insert the tuple into the relation
			    	rid = insertTuple(f, t);
			    	if (updateIndexes)
			    	{
			    		// if updating indexes while inserting, index the tuple in all related indexes
			    		indexer.UpdateIndexes(t, rid);
			    	}
			    	tupleCnt++;
			    }
	    	}
	    }	
	    br.close();	// done reading file
        if (indexer != null) {
            indexer.close();
        }
	    if (successful)
	    {
	    	// report statistics
	    	System.out.format("Inserted %d tuples into the database\n", tupleCnt);
	    	if (updateIndexes)
	    	{
		    	System.out.format("Updated %d indexes.\n", indexer.IndexCount());
	    	}
	    	
	    	// Create the Schema in the database
	    	Schema.insertSchema(relName, ts.attrType, VAFile.BITS_PER_DIMENSTION);
	    	return ts;
	    }
	    return null;
	}
	
	/*
	 * Method gets the attribute types from the data file
	 */
	static Schema getAttribTypes(String typesLine, int attrCnt)
	{
	    String[] attrArray = typesLine.split(" ");
	    int strCnt = 0;
	    if (attrArray.length != attrCnt)
	    {
	    	System.err.println("Number of attributes and number of attribute types does not match. Invalid data file.");
	    	return null;
	    }
	    AttrType[] attrTypes = new AttrType[attrCnt];
		for (int i=0;i<attrCnt;i++)
		{
			switch (Integer.parseInt(attrArray[i]))
			{
			case 1:	// integer
				attrTypes[i] = new AttrType(AttrType.attrInteger);
				break;
			case 2:	// real
				attrTypes[i] = new AttrType(AttrType.attrReal);
				break;
			case 3:	// string
				attrTypes[i] = new AttrType(AttrType.attrString);
				strCnt++;
				break;
			case 4: 	// 100D vector
				attrTypes[i] = new AttrType(AttrType.attrVector100D);
				break;
			default:
				System.err.println("Unrecognized attribute type. Invalid data file.");
		    	return null;
			}
		}
		short[] strSizes = new short[strCnt];
		for (int i=0;i<strCnt;i++)
		{
			strSizes[i] = Schema.SCHEMA_STRING_LENGTH;
		}
		return new Schema("", attrTypes, strSizes, 0);
	}

	/*
	 * Prepares a Tuple object for use in inserting records
	 */
	public static Tuple prepTuple(int attrCnt, AttrType[] attrTypes, short[] strSizes)
	{
	    // prepare tuple
	    Tuple t = new Tuple();

	    try {
	      t.setHdr((short) attrCnt, attrTypes, strSizes);
	    }
	    catch (Exception e) {
	      System.err.println("Error preparing tuple.");
	      return null;
	    }

	    int size = t.size();

	    t = new Tuple(size);
	    try {
	    	t.setHdr((short) attrCnt, attrTypes, strSizes);
	    }
	    catch (Exception e) {
		      System.err.println("Error preparing tuple.");
		      return null;
	    }
	    return t;
	}

	/*
	 * Method creates a Tuple from the n lines read from the data file where
	 * n = the number of attributes
	 */
	private static Tuple getTuple(Tuple t, Schema ts, String[] values, int attrCnt)
	{
		int intVal;
		float floatVal;
		Vector100Dtype vector;
		
		try
		{
			for (int i=0;i<attrCnt;i++)
			{
				switch (ts.attrType[i].attrType)
				{
				case AttrType.attrInteger:
					intVal = Integer.parseInt(values[i]);
					t.setIntFld(i+1, intVal);
					break;
				case AttrType.attrReal:
					floatVal = Float.parseFloat(values[i]);
					t.setFloFld(i+1, floatVal);
					break;
				case AttrType.attrString:
					t.setStrFld(i+1, values[i]);
					break;
				case AttrType.attrVector100D:
					vector = new Vector100Dtype(values[i]);
					t.set100DVectFld(i+1, vector);
					break;
				}
			}
		}
		catch (Exception e)
		{
			System.err.println("Could not create tuple. Invalid data file.");
			return null;
		}
		return t;
	}

	/*
	 * Method inserts a tuple into the heapfile
	 */
	private static RID insertTuple(Heapfile f, Tuple t)
	{
		RID rid = null;
		try
		{
			rid = f.insertRecord(t.returnTupleByteArray());
		}
		catch (Exception e)
		{
			System.err.println("Error inserting a record");
			return null;
		}
		return rid;
	}

	/*
	 * Identify the fields that are vectors so that we can create a VAFile index
	 * for them
	 */
	private static int[] getVectorFlds(Schema ts)
	{
		int cnt=0;
		for (int i=0;i<ts.attrType.length;i++)
		{
			if (ts.attrType[i].attrType == AttrType.attrVector100D)
			{
				cnt++;
			}
		}
		int[] vflds = new int[cnt];
		cnt = 0;
		for (int i=0;i<ts.attrType.length;i++)
		{
			if (ts.attrType[i].attrType == AttrType.attrVector100D)
			{
				vflds[cnt] = i+1;
				cnt++;
			}
		}
		return vflds;
	}
}


